﻿using System;
using System.Dynamic;
using System.Linq.Expressions;

namespace SemanticEx
{
	/// <summary>
	/// A dynamic expression must have a DynamicMetaObjectBinder class to act on.
	/// This class ensures that two dynamic values can be added together, and
	/// creates the result.
	/// </summary>
	public class SimpleBinaryOperationBinder : BinaryOperationBinder
	{
		public SimpleBinaryOperationBinder(ExpressionType expressionType)
			: base(expressionType)
		{
		}

		/// <summary>
		/// This is called to make sure the two dynamic values are added together somehow.
		/// </summary>
		public override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion)
		{
			if (!target.HasValue || !arg.HasValue)
			{
				return Defer(target, arg);
			}

			var targetLimitType = (Operation == ExpressionType.Power) ? typeof(double) : target.LimitType;
			
			var restrictions = target.Restrictions
				.Merge(arg.Restrictions)
				.Merge(BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType))
				.Merge(BindingRestrictions.GetTypeRestriction(arg.Expression, arg.LimitType));

			var targetExpr = target.Expression.ChangeType(targetLimitType).Convert(targetLimitType);
			var argExpr = arg.Expression.ChangeType(targetLimitType).Convert(targetLimitType);

			return new DynamicMetaObject(ExpressionEx.EnsureObjectResult(Expression.MakeBinary(Operation, targetExpr, argExpr)), restrictions);
		}
	}
}